package myjson

import (
    "encoding/json"
    "fmt"
    "reflect"
)

type JsonObject struct {
	value 				interface{}
}

func NewJsonObject() *JsonObject {
	result := &JsonObject{}
	result.value = make(map[string]*JsonObject)
	return result
}

func NewJsonArray() *JsonObject {
	result := &JsonObject{}
	result.value = make([]*JsonObject, 0)
	return result
}

func (jobj *JsonObject) UnmarshalJSON(src []byte) error {
	if src[0] == '{' && src[len(src) - 1] == '}' {
		tmp := make(map[string]*JsonObject)
		err := json.Unmarshal(src, &tmp)
		jobj.value = tmp
		return err
	}
	if src[0] == '[' && src[len(src) - 1] == ']' {
		tmp := make([]*JsonObject, 0)
		err := json.Unmarshal(src, &tmp)
		jobj.value = tmp
		return err
	}
	return json.Unmarshal(src, &jobj.value)
}

func (jobj JsonObject) MarshalJSON() ([]byte, error) {
	return json.Marshal(jobj.value)
}

func (jobj JsonObject) AsInt() int64 {
	result := jobj.value.(float64)
	return int64(result)
}

func (jobj JsonObject) AsFloat() float64 {
	return jobj.value.(float64)
}

func (jobj JsonObject) AsBool() bool {
	return jobj.value.(bool)
}

func (jobj JsonObject) AsString() string {
	return jobj.value.(string)
}

func (jobj JsonObject) AsStrut(dst interface{}) {
	dstV := reflect.ValueOf(dst)
	if dstV.Kind() != reflect.Ptr || dstV.IsNil() {
		panic(fmt.Errorf("not ptr or is nil"))
	}
	dstV = dstV.Elem()
	if dstV.Kind() != reflect.Struct {
		panic(fmt.Errorf("not struct"))
	}
	jobj.toStructValue(dstV)
}

func (jobj JsonObject) AsMap() map[string]*JsonObject {
	return jobj.value.(map[string]*JsonObject)
}

func (jobj JsonObject) AsArray() []*JsonObject {
	return jobj.value.([]*JsonObject)
}

func (jobj JsonObject) Get(key string) *JsonObject {
	data := jobj.AsMap()
	return data[key]
}

func (jobj *JsonObject) Put(key string, value interface{}) {
	mapData := jobj.AsMap()
	if v, ok := value.(*JsonObject); ok {
		mapData[key] = v
		return
	}
	tmpObj := &JsonObject{}
	tmpObj.valueToObj(reflect.ValueOf(value))
	mapData[key] = tmpObj
}

func (jobj *JsonObject) Append(value interface{}) {
	arrayData := jobj.AsArray()
	if v, ok := value.(*JsonObject); ok {
		arrayData = append(arrayData, v)
		jobj.value = arrayData
		return
	}
	tmpObj := &JsonObject{}
	tmpObj.valueToObj(reflect.ValueOf(value))
	arrayData = append(arrayData, tmpObj)
	jobj.value = arrayData
}

func (jobj JsonObject) ToJsonString() string {
	result, _ := json.Marshal(jobj)
	return string(result)
}

func (jobj JsonObject) toValue(value reflect.Value) {
	switch value.Kind() {
	case reflect.Bool:
		value.SetBool(jobj.AsBool())
	case reflect.String:
		value.SetString(jobj.AsString())
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		value.SetInt(jobj.AsInt())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		value.SetUint(uint64(jobj.AsInt()))
	case reflect.Float32, reflect.Float64:
		value.SetFloat(jobj.AsFloat())
	case reflect.Slice:
		jobj.toSliceValue(value)
	case reflect.Map:
		jobj.toMapValue(value)
	case reflect.Struct:
		jobj.toStructValue(value)
	case reflect.Ptr:
		eType := value.Type().Elem()
		value.Set(reflect.New(eType))
		jobj.toValue(value.Elem())
	}
}

func (jobj JsonObject) toStructValue(value reflect.Value) {
	data := jobj.AsMap()
	for i := 0; i < value.NumField(); i++ {
		fieldT := value.Type().Field(i)
		fieldName := firstToLower(fieldT.Name)
		fieldData, ok := data[fieldName]
		if !ok {
			continue
		}
		fieldV := value.Field(i)
		fieldData.toValue(fieldV)
	}
}

func (jobj JsonObject) toMapValue(value reflect.Value) {
	data := jobj.AsMap()
	tmpMap := reflect.MakeMap(value.Type())
	for k, v := range data {
		kV := reflect.ValueOf(k)
		VV := reflect.New(value.Type().Elem())
		v.toValue(VV.Elem())
		tmpMap.SetMapIndex(kV, VV.Elem())
	}
	value.Set(tmpMap)
}

func (jobj JsonObject) toSliceValue(value reflect.Value) {
	tmpArr := jobj.AsArray()
	tmpS := reflect.MakeSlice(value.Type(), len(tmpArr), len(tmpArr))
	for i := 0; i < tmpS.Len(); i++ {
		tmpV := tmpS.Index(i)
		tmpArr[i].toValue(tmpV)
	}
	value.Set(tmpS)
}

func (jobj *JsonObject) valueToObj(value reflect.Value) {
	switch value.Kind() {
	case reflect.Bool:
		jobj.value = value.Bool()
	case reflect.String:
		jobj.value = value.String()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		jobj.value = float64(value.Int())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		jobj.value = float64(value.Uint())
	case reflect.Float32, reflect.Float64:
		jobj.value = value.Float()
	case reflect.Slice:
		jobj.sliceValueToObj(value)
	case reflect.Map:
		jobj.mapValueToObj(value)
	case reflect.Struct:
		jobj.structValueToObj(value)
	case reflect.Ptr:
		jobj.valueToObj(value.Elem())
	}
}

func (jobj *JsonObject) sliceValueToObj(value reflect.Value) {
	tmpSlice := make([]JsonObject, value.Len())
	for i := 0; i < value.Len(); i++ {
		tmpObj := JsonObject{}
		tmpObj.valueToObj(value.Index(i))
		tmpSlice[i] = tmpObj
	}
	jobj.value = tmpSlice
}

func (jobj *JsonObject) structValueToObj(value reflect.Value) {
	if value.Type() == reflect.TypeOf(JsonObject{}) {
		*jobj = value.Interface().(JsonObject)
		return
	}
	data := make(map[string]*JsonObject)
	for i := 0; i < value.NumField(); i++ {
		fieldT := value.Type().Field(i)
		fieldName := firstToLower(fieldT.Name)
		fieldV := value.Field(i)
		fieldData := &JsonObject{}
		fieldData.valueToObj(fieldV)
		data[fieldName] = fieldData
	}
	jobj.value = data
}

func (jobj *JsonObject) mapValueToObj(value reflect.Value) {
	data := make(map[string]*JsonObject)
	keys := value.MapKeys()
	for _, key := range keys {
		tmpObj := &JsonObject{}
		tmpObj.valueToObj(value.MapIndex(key))
		data[key.String()] = tmpObj
	}
	jobj.value = data
}

func firstToLower(src string) string{
	if src == "" {
		return src
	}
	result := []byte(src)
	if result[0] >= 'A' && result[0] <= 'Z' {
		result[0] = 'a' + result[0] - 'A'
	}
	return string(result)
}